Packer 入門として AWS Systems Manager エージェントがインストール済みの RHEL 7 AMI を作成してみた
コンバンハ、千葉(幸)です。
最近、RHEL 7.7 の EC2 インスタンスを触りたい機会がありました。 Systems Manager を使って操作する気まんまんですが、Red Hat 社から標準で提供されている AMI ではエージェントがプリインストールされていません。
「インスタンスを作成してからインストールする」でもいいのですが、せっかくなので前々から触ってみたいと思っていた Packer を使用してエージェントインストール済みの AMI を作ってみることにします。
パカっていきましょう。
Packer とは
Terraform などでお馴染みの HashiCorp 社が提供している、マシンイメージの作成・管理を行うコマンドラインツールです。 AWS に限らず様々な環境で使用できます。
今回やりたいことは AMI の作成ですので、そこに限定し、かつ今回の構成にあわせてイメージ図を書くとこのようになります。
テンプレート形式でもろもろの値を定義し、それを指定してビルドすることで大まかに以下の流れが実行されます。
- ソース AMI からテンポラリなインスタンスの作成
- インスタンスに操作端末から SSH 接続しシェルの実行
- 設定が完了したインスタンスから AMI の作成
- インスタンスや関連するテンポラリなリソースの削除
前提条件
インスタンスや AMI などのリソースの作成を伴いますので、なにがしかの形で IAM の認証情報が必要となります。
今回はAdministratorAccess
を持つ IAM ユーザーのクレデンシャルを用意しました。AWS CLI のプロファイルで指定済みです。
[default] region = ap-northeast-1
[default] aws_access_key_id = AKIAQ3BIIHXXXXXXXXXX aws_secret_access_key = nb4QLVBkQRXXXXXXXXXXXXXXXXXXXXXXXXXXXX
IAM ロールを引き受ける形でも実行可能です。他の手段については以下を参照してください。
Packer のインストール
筆者の環境は mac のため、以下でインストールを行います。
% brew install packer
バージョンは以下の通りです。
% packer --version 1.7.2
テンプレートファイルの作成
以下を作成しました。
{ "builders": [ { "type": "amazon-ebs", "region": "ap-northeast-1", "instance_type": "t2.micro", "ssh_username": "ec2-user", "source_ami_filter": { "filters": { "name": "RHEL-7.7_HVM-*-x86_64-1-Hourly2-GP2" }, "owners": ["309956199498"], "most_recent": true }, "ami_name": "RHEL-7.7-{{isotime | clean_resource_name}}", "tags": { "Base_AMI_ID": "{{ .SourceAMI }}", "Base_AMI_NAME": "{{ .SourceAMIName }}" } } ], "provisioners": [ { "type": "shell", "inline": [ "sudo yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm" ] } ] }
ハイライト部を補足します。
source_ami_filter により最新の AMI を指定
こちらのエントリを参考にしました。今回は RHEL 7.7 の最新の AMI を指定する形式としています。
対象の AMI の所有者は以下画面から ami-id で検索して確認しました。
Systems Manager エージェントのインストールコマンド
以下ページに記載のコマンドを指定しています。
余談ですが、取得先が S3 なので、プライベートサブネットのインスタンスでも VPC エンドポイントを経由すれば問題なくインストールできそうですね。
テンプレート構文の検証
packer validate <テンプレートファイル名>
で確認を行います。特に何も出力されなければ問題ありません。
% packer validate rhel7_ssmagent.json %
誤った構文の場合には例えば以下のように表示されます。
% packer validate rhel7_ssmagent.json Failed to parse file as legacy JSON template: if you are using an HCL template, check your file extensions; they should be either *.pkr.hcl or *.pkr.json; see the docs for more details: https://www.packer.io/docs/templates/hcl_templates. Original error: Error parsing JSON: invalid character '"' after object key:value pair At line 25, column 10 (offset 579): 24: "type": "shell" 25: " ^
使用可能なオプションなどは以下を参照してください。
% packer validate Usage: packer validate [options] TEMPLATE Checks the template is valid by parsing the template and also checking the configuration with the various builders, provisioners, etc. If it is not valid, the errors will be shown and the command will exit with a non-zero exit status. If it is valid, it will exit with a zero exit status. Options: -syntax-only Only check syntax. Do not verify config of the template. -except=foo,bar,baz Validate all builds other than these. -machine-readable Produce machine-readable output. -only=foo,bar,baz Validate only these builds. -var 'key=value' Variable for templates, can be used multiple times. -var-file=path JSON or HCL2 file containing user variables.
ビルドの実行
早速実行していきます。
packer build <テンプレートファイル名>
で実行できます。
失敗1:デフォルト VPC がない
幸先よく失敗します。
% packer build rhel7_ssmagent.json amazon-ebs: output will be in this color. ==> amazon-ebs: Prevalidating any provided VPC information ==> amazon-ebs: Prevalidating AMI Name: RHEL-7.7-2021-06-04T13-20-35Z amazon-ebs: Found Image ID: ami-04a12c0fbaeac005e ==> amazon-ebs: Creating temporary keypair: packer_60ba28a3-5792-7fa7-c47c-02568ac89612 ==> amazon-ebs: Creating temporary security group for this instance: packer_60ba28a4-013a-cdab-ca76-03188bc630f2 ==> amazon-ebs: VPCIdNotSpecified: No default VPC for this user ==> amazon-ebs: status code: 400, request id: cdeeff9c-f34a-4c8b-a71c-b39c62bfe56a ==> amazon-ebs: Deleting temporary keypair... Build 'amazon-ebs' errored after 2 seconds 48 milliseconds: VPCIdNotSpecified: No default VPC for this user status code: 400, request id: cdeeff9c-f34a-4c8b-a71c-b39c62bfe56a ==> Wait completed after 2 seconds 49 milliseconds ==> Some builds didn't complete successfully and had errors: --> amazon-ebs: VPCIdNotSpecified: No default VPC for this user status code: 400, request id: cdeeff9c-f34a-4c8b-a71c-b39c62bfe56a ==> Builds finished but no artifacts were created.
AMI のベースとなるテンポラリなインスタンスは、配置先の指定がないとデフォルト VPC に作成が試みられます。私の環境ではデフォルト VPC が削除済みであるためにエラーが発生しました。
ということで、テンプレートファイルを以下に修正しました。
{ "builders": [ { "type": "amazon-ebs", "region": "ap-northeast-1", "instance_type": "t2.micro", "ssh_username": "ec2-user", "source_ami_filter": { "filters": { "name": "RHEL-7.7_HVM-*-x86_64-1-Hourly2-GP2" }, "owners": ["309956199498"], "most_recent": true }, "ami_name": "RHEL-7.7-{{isotime | clean_resource_name}}", "tags": { "Base_AMI_ID": "{{ .SourceAMI }}", "Base_AMI_NAME": "{{ .SourceAMIName }}" }, "vpc_id": "vpc-0e4acafc38414468c", "subnet_id": "subnet-0caa45223899b4b73" } ], "provisioners": [ { "type": "shell", "inline": [ "sudo yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm" ] } ]
テンポラリなインスタンスの作成先を特定の VPC、サブネット(パブリック)に指定しました。
失敗2:パブリック IP アドレスが付与されない
上記のテンプレートを指定して再度ビルドを行います。
% packer build rhel7_ssmagent_kai.json amazon-ebs: output will be in this color. ==> amazon-ebs: Prevalidating any provided VPC information ==> amazon-ebs: Prevalidating AMI Name: RHEL-7.7-2021-06-04T13-27-43Z amazon-ebs: Found Image ID: ami-04a12c0fbaeac005e ==> amazon-ebs: Creating temporary keypair: packer_60ba2a4f-fc54-7e58-f68c-d44979599175 ==> amazon-ebs: Creating temporary security group for this instance: packer_60ba2a51-1c72-0015-9331-d2d157427a1d ==> amazon-ebs: Authorizing access to port 22 from [0.0.0.0/0] in the temporary security groups... ==> amazon-ebs: Launching a source AWS instance... ==> amazon-ebs: Adding tags to source instance amazon-ebs: Adding tag: "Name": "Packer Builder" amazon-ebs: Instance ID: i-079c3c8497366fea9 ==> amazon-ebs: Waiting for instance (i-079c3c8497366fea9) to become ready... ==> amazon-ebs: Using ssh communicator to connect: 192.168.0.145 ==> amazon-ebs: Waiting for SSH to become available... ==> amazon-ebs: Timeout waiting for SSH. ==> amazon-ebs: Terminating the source AWS instance... ==> amazon-ebs: Cleaning up any extra volumes... ==> amazon-ebs: No volumes to clean up, skipping ==> amazon-ebs: Deleting temporary security group... ==> amazon-ebs: Deleting temporary keypair... Build 'amazon-ebs' errored after 7 minutes 7 seconds: Timeout waiting for SSH. ==> Wait completed after 7 minutes 7 seconds ==> Some builds didn't complete successfully and had errors: --> amazon-ebs: Timeout waiting for SSH. ==> Builds finished but no artifacts were created.
テンポラリな SecuriryGroup(0.0.0.0/0 で SSHが開いてる……!)、インスタンスの作成が行われます。その後に SSH 接続が試みられていますが、接続先としてインスタンスのプライベート IP アドレスが指定されています。
これでは当然 SSH 接続できませんので、タイムアウトしています。
今回私が指定したサブネットはパブリックサブネットですが、サブネットでのパブリック IP アドレスの自動割り当て設定は無効となっています。テンポラリなインスタンスに対して明示的に割り当ての許可が必要です。
ということで、テンプレートを以下のように修正しました。
{ "builders": [ { "type": "amazon-ebs", "region": "ap-northeast-1", "instance_type": "t2.micro", "ssh_username": "ec2-user", "source_ami_filter": { "filters": { "name": "RHEL-7.7_HVM-*-x86_64-1-Hourly2-GP2" }, "owners": ["309956199498"], "most_recent": true }, "ami_name": "RHEL-7.7-{{isotime | clean_resource_name}}", "tags": { "Base_AMI_ID": "{{ .SourceAMI }}", "Base_AMI_NAME": "{{ .SourceAMIName }}" }, "vpc_id": "vpc-0e4acafc38414468c", "subnet_id": "subnet-0caa45223899b4b73", "associate_public_ip_address": true, "security_group_ids": "sg-0a597e5b2a9a1d86d" } ], "provisioners": [ { "type": "shell", "inline": [ "sudo yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm" ] } ] }
パブリック IP アドレスの自動割り当てを許可したほか、既存の SecuriryGroup を使用するように変更しました。
この SecuriryGroup では 22 ポートのインバウンドをマイ IP (自身の拠点のグローバル IP )に限定しています。
ようやくビルド成功
上記のテンプレートを指定してビルドを行うことで、ようやく成功しました。
せっかくなのでプロセスを分割して見ていきます。
% packer build rhel7_ssmagent_kaikai.json amazon-ebs: output will be in this color. ==> amazon-ebs: Prevalidating any provided VPC information ==> amazon-ebs: Prevalidating AMI Name: RHEL-7.7-2021-06-04T13-52-43Z amazon-ebs: Found Image ID: ami-04a12c0fbaeac005e ==> amazon-ebs: Creating temporary keypair: packer_60ba302b-54e3-60f5-5218-b9ef9614466c ==> amazon-ebs: Launching a source AWS instance... ==> amazon-ebs: Adding tags to source instance amazon-ebs: Adding tag: "Name": "Packer Builder" amazon-ebs: Instance ID: i-04a7f0a409d3fa99f ==> amazon-ebs: Waiting for instance (i-04a7f0a409d3fa99f) to become ready... ==> amazon-ebs: Using ssh communicator to connect: 54.95.37.143 ==> amazon-ebs: Waiting for SSH to become available... ==> amazon-ebs: Connected to SSH!
↑テンポラリなインスタンスが立ち上がり、自端末からグローバル IP アドレスを指定して SSH 接続が成功します。接続に使用するキーペアもテンポラリなものです。ここまで 1 分もかからなかったかと思います。
==> amazon-ebs: Provisioning with shell script: /var/folders/5_/v4p285913wq2scrrq28g14nc0000gq/T/packer-shell417327082 amazon-ebs: Loaded plugins: amazon-id, search-disabled-repos amazon-ebs: Examining /var/tmp/yum-root-GMKzK7/amazon-ssm-agent.rpm: amazon-ssm-agent-3.0.1209.0-1.x86_64 amazon-ebs: Marking /var/tmp/yum-root-GMKzK7/amazon-ssm-agent.rpm to be installed amazon-ebs: Resolving Dependencies amazon-ebs: --> Running transaction check amazon-ebs: ---> Package amazon-ssm-agent.x86_64 0:3.0.1209.0-1 will be installed amazon-ebs: --> Finished Dependency Resolution amazon-ebs: amazon-ebs: Dependencies Resolved amazon-ebs: amazon-ebs: ================================================================================ amazon-ebs: Package Arch Version Repository Size amazon-ebs: ================================================================================ amazon-ebs: Installing: amazon-ebs: amazon-ssm-agent x86_64 3.0.1209.0-1 /amazon-ssm-agent 108 M amazon-ebs: amazon-ebs: Transaction Summary amazon-ebs: ================================================================================ amazon-ebs: Install 1 Package amazon-ebs: amazon-ebs: Total size: 108 M amazon-ebs: Installed size: 108 M amazon-ebs: Downloading packages: amazon-ebs: Running transaction check amazon-ebs: Running transaction test amazon-ebs: Transaction test succeeded amazon-ebs: Running transaction amazon-ebs: Installing : amazon-ssm-agent-3.0.1209.0-1.x86_64 1/1 amazon-ebs: Created symlink from /etc/systemd/system/multi-user.target.wants/amazon-ssm-agent.service to /etc/systemd/system/amazon-ssm-agent.service. amazon-ebs: Verifying : amazon-ssm-agent-3.0.1209.0-1.x86_64 1/1 amazon-ebs: amazon-ebs: Installed: amazon-ebs: amazon-ssm-agent.x86_64 0:3.0.1209.0-1 amazon-ebs: amazon-ebs: Complete!
↑SSM エージェントのインストールがあっという間に終わります。
==> amazon-ebs: Stopping the source instance... amazon-ebs: Stopping instance ==> amazon-ebs: Waiting for the instance to stop... ==> amazon-ebs: Creating AMI RHEL-7.7-2021-06-04T13-52-43Z from instance i-04a7f0a409d3fa99f amazon-ebs: AMI: ami-063ec89988bc36945 ==> amazon-ebs: Waiting for AMI to become ready... ==> amazon-ebs: Adding tags to AMI (ami-063ec89988bc36945)... ==> amazon-ebs: Tagging snapshot: snap-0d806e1437959e2d3 ==> amazon-ebs: Creating AMI tags amazon-ebs: Adding tag: "Base_AMI_ID": "ami-04a12c0fbaeac005e" amazon-ebs: Adding tag: "Base_AMI_NAME": "RHEL-7.7_HVM-20191028-x86_64-1-Hourly2-GP2" ==> amazon-ebs: Creating snapshot tags ==> amazon-ebs: Terminating the source AWS instance... ==> amazon-ebs: Cleaning up any extra volumes... ==> amazon-ebs: No volumes to clean up, skipping ==> amazon-ebs: Deleting temporary keypair... Build 'amazon-ebs' finished after 10 minutes 42 seconds. ==> Wait completed after 10 minutes 42 seconds ==> Builds finished. The artifacts of successful builds are: --> amazon-ebs: AMIs were created: ap-northeast-1: ami-063ec89988bc36945
↑テンポラリなインスタンスはあっという間に停止され、AMI の作成処理が走ります。ビルドの実行から停止まで 1 分強程度の素早い処理でした。 AMI の作成は流石に多少時間がかかり、10 GiB のスナップショットを持つ AMI が 10 分程度で作成されました。
AMI の作成が完了したらテンポラリなインスタンス、キーペアは削除され一通りの処理が完了です。
指定した通りの内容で AMI が作成されました。
作成したAMI からインスタンスの作成
念のためきちんと Systems Manager エージェントが機能するかを確認します。
Packer で作成した AMI から新規インスタンスを立ち上げました。パブリックサブネットに配置し、必要な権限を持つ IAM ロールをアタッチしています。
インスタンスに対して Systems Manager のセッションマネージャー接続を試みると……
この通り、きちんと接続できました。
やったね!
終わりに
今更ながら Packer に入門してみました。先人のブログを眺めていて「一時的なインスタンスが作成されてそこから AMI が取得される」というところまではイメージがついていたのですが、
- インスタンスはどこに作成されるの?
- →(指定なしだとデフォルト VPC。指定も可)
- インスタンス上での操作はどう行われるの?
- →(操作端末から、指定した OS ユーザーで SSH して実行)
というのが分かっていなかったので、実際に手を動かしてみてようやく理解できました。
せっかく覚えたので、今後も積極的に使っていきたいと思います。
以上、 チバユキ (@batchicchi) がお送りしました。